﻿using System;
using System.Collections.Generic;

namespace PlottingBasics.LINQHelpers
{
    public static class Difference
    {
        /// <summary>
        /// Returns an enumerator that calcs the delta between two adjacent elements. It will always return
        /// the delta in the first item. So the last delta in the sequence will alway sbe zero.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="GetValue"></param>
        /// <returns></returns>
        public static IEnumerable<double> DeltaFirst<T>(this IEnumerable<T> source, Func<T, double> GetValue)
        {
            return new DeltaItr<T>(source, GetValue, true);
        }

        /// <summary>
        /// Retursn the deltas between adjacent memebers of the sequence. Places them after the delta, so the first
        /// member of the sequence will always be zero.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="GetValue"></param>
        /// <returns></returns>
        public static IEnumerable<double> DeltaLast<T>(this IEnumerable<T> source, Func<T, double> GetValue)
        {
            return new DeltaItr<T>(source, GetValue, false);
        }

        public static IEnumerable<double> Delta<T>(this IEnumerable<T> source, Func<T, double> GetValue)
        {
            return new DeltaItr<T>(source, GetValue);
        }

        /// <summary>
        /// Iterator class that does the actual delta calculations
        /// </summary>
        /// <typeparam name="T"></typeparam>
        class DeltaItr<T> : IEnumerable<double>
        {
            private IEnumerable<T> _source;
            private Func<T, double> _getValue;
            private bool _putDeltaFirst;
            private bool _addExtra = false;

            public DeltaItr(IEnumerable<T> source, Func<T, double> GetValue, bool putFirst)
            {
                // TODO: Complete member initialization
                this._source = source;
                this._getValue = GetValue;
                this._putDeltaFirst = putFirst;
                this._addExtra = true;
            }

            /// <summary>
            /// Create, no extra element is going ot be shown.
            /// </summary>
            /// <param name="source"></param>
            /// <param name="GetValue"></param>
            public DeltaItr(IEnumerable<T> source, Func<T, double> GetValue)
            {
                this._source = source;
                this._getValue = GetValue;
            }

            public System.Collections.Generic.IEnumerator<double> GetEnumerator()
            {
                bool first = true;
                double last = 0.0;
                foreach (var item in _source)
                {
                    double thisVal = _getValue(item);
                    double delta = thisVal - last;
                    if (first & !_putDeltaFirst & _addExtra)
                    {
                        yield return 0.0;
                    }
                    if (!first)
                    {
                        yield return (thisVal - last);
                    }
                    first = false;
                    last = thisVal;
                }

                if (_putDeltaFirst && !first && _addExtra)
                {
                    yield return 0.0;
                }
            }

            #region IEnumerable Members

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                throw new NotImplementedException();
            }

            #endregion
        }

    }
}
